fix(docs): FieldSchema.extend() does not exist — the FAQ recommended a call that throws - #3890
Merged
Merged
Conversation
…a call that throws #3882 brought content/docs under the example type-check gate and left the open question: of the blocks still unmarked, is any GENUINE ROT rather than a fragment? Swept them. One real one. deployment/troubleshooting.mdx answered "How do I extend a built-in schema?" with `FieldSchema.extend({ … })`. FieldSchema carries a .transform() that lowers author-facing sugar at parse time, so it is a ZodPipe, not a ZodObject — `.extend` is undefined on it (verified against the built package). Anyone following the FAQ got a "not a function" throw. The example also used `z` without importing it. Rewritten to FieldSchema.in.extend({ … }) — `.in` is the input object the pipe wraps — verified to parse, marked so the gate holds it, and it now says why (same for ObjectSchema/ActionSchema) and warns that the extended schema no longer runs the transform. The sweep's METHOD is recorded in the gate docstring, because two traps make a naive pass report a confident "nothing found": 1. tsc stops after syntactic diagnostics and never runs the semantic pass. Marking all 780 blocks at once let ~200 broken fragments suppress type-checking for every other block; that run returned only TS1xxx codes, which reads exactly like "no rot" and proves nothing. Caught by injecting a deliberate type error and watching it go unreported. 2. Unimported type names resolve against the DOM lib — Plugin, Event, Response, Storage all exist there. A block missing its import reports "'version' does not exist in type 'Plugin'" against lib.dom's Plugin: an artefact, not drift. Three of the strongest-looking candidates were exactly this. After both corrections the rest are fragments, plus config-resolution.mdx, whose aspirational snippets a callout on the page already labels as design intent. Gate: 196 -> 197 checked examples. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 104 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…ch FieldSchema declaration you get CI rejected the first fix: `FieldSchema.in.extend(…)` passed locally and failed with "Property 'in' does not exist on type 'ZodObject<…>'" on the runner. The two builds emit DIFFERENT declarations for the same source — locally z.ZodPipe<z.ZodObject<…>>, in CI a plain z.ZodObject. The runtime is unambiguous: after `rm -rf dist && pnpm build`, FieldSchema is a `bound ZodPipe` with `.extend === undefined` and `.in` a ZodObject. So CI's declaration contradicts the value it describes — `.extend()` type-checks there and throws at runtime. That is a real spec-side divergence (likely inference instability in the DTS bundling of these very large zod types) and deserves its own investigation; it is not something this docs fix should paper over. So the checked example now uses only `.parse()`: parse with FieldSchema, validate the additions alongside it. Correct under either declaration, verified to run, and better advice anyway — composition keeps the transform, which `.extend()` discards. `.in.extend()` stays in prose as the merged-schema route with that caveat. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ajwvrmd1hDC9RBofYBhGuR
os-zhuang
marked this pull request as ready for review
July 28, 2026 15:16
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#3882 brought
content/docsunder the example type-check gate and left an open question: of the ~600 blocks still unmarked, is any of it genuine rot rather than a fragment? Swept them. One real one.The rot
content/docs/deployment/troubleshooting.mdxanswered "How do I extend a built-in schema?" — a direct FAQ, not an illustrative sketch — with:FieldSchemacarries a.transform()that lowers author-facing sugar at parse time, which makes it aZodPipe, not aZodObject. Verified against the built package:So anyone following the FAQ got
FieldSchema.extend is not a function. Rewritten toFieldSchema.in.extend({ … }), verified to parse, and marked so the gate holds it (196 → 197 checked examples). The answer now also says why — the same applies toObjectSchema/ActionSchema— and warns that the extended schema no longer runs the transform.The method matters more than the finding, so it's now in the gate's docstring
Two traps make a naive sweep report a confident "nothing found". Both caught me.
1.
tscstops after syntactic diagnostics and never runs the semantic pass.I marked all 780 blocks and compiled. Result: 1178 diagnostics, every one a TS1xxx syntax code, zero TS2xxx. That reads exactly like "no rot anywhere" — and I nearly reported it. It's an artefact: ~200 syntactically-broken fragments suppressed type-checking for the entire program, including the 597 blocks that parse fine.
Caught it by injecting a deliberate type error into a known-good block and watching it go unreported. Excluding the syntax failures and re-running turned up 1000+ semantic diagnostics that had been invisible.
This is the same shape as the failure the liveness README documents — "a strong negative claim resting on a search whose result set was silently truncated or filtered" — one layer down.
2. Unimported type names resolve against the DOM lib.
Plugin,Event,Response,Storage,Selectionall exist inlib.dom. A block that omits its import reports:…against the browser's
Plugin, while the realPlugincontract (contracts/plugin-validator.ts) hasversion?,init?,destroy?and an index signature. Three of the strongest-looking candidates —plugins/index.mdx, two inkernel/services.mdx— were exactly this. Artefacts, not drift.What the rest turned out to be
columns: [...]subtrees, YAML in atsfence, blocks referencing symbols defined in neighbouring blocks. The gate's opt-in design already anticipates these.@objectstack/core,zod,hono, relative project paths. A harness limit, not rot: the tsconfigpathsmap only wires@objectstack/spec. Widening it is a separate, plausible follow-up.protocol/kernel/config-resolution.mdx— itsdatabase:/http:keys genuinely aren't ondefineStack, but a callout at the top of the page already labels those snippets "design intent, not paste-ready code" and names those very keys. Correctly unmarked; no change.Verification
pnpm --filter @objectstack/spec check:skill-examples—✅ 197 prose examples type-check.pnpm check:doc-authoring— 213 files clean..in.extendverified by executing it against the built package (output above), not just by type-checking.Generated by Claude Code